package com.element.security.server.global.token;

import com.element.security.constant.AuthGrantTypeEnum;
import com.element.security.server.config.AuthServerTokenConfig;
import com.element.security.server.global.GlobalOAuth2RequestFactory;
import com.element.security.server.global.granter.ParamGranter;
import com.element.security.server.service.IAuthCodeInMemoryService;
import com.element.security.server.service.IGrantTypeService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.CompositeTokenGranter;
import org.springframework.security.oauth2.provider.OAuth2RequestFactory;
import org.springframework.security.oauth2.provider.TokenGranter;
import org.springframework.security.oauth2.provider.client.ClientCredentialsTokenGranter;
import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices;
import org.springframework.security.oauth2.provider.code.AuthorizationCodeTokenGranter;
import org.springframework.security.oauth2.provider.implicit.ImplicitTokenGranter;
import org.springframework.security.oauth2.provider.refresh.RefreshTokenGranter;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * @auther zhangwj
 * @date 2021/4/16 下午8:07
 */
@Configuration
public class TokenGranterConfig {

    @Resource
    private IAuthCodeInMemoryService authCodeInMemoryService;

    @Resource
    private AuthenticationManager authenticationManager;

    @Resource
    private AuthServerTokenConfig authServerTokenConfig;

    @Resource
    private ClientDetailsService clientDetailsService;

    @Resource
    private IGrantTypeService grantTypeService;

    @Bean
    public TokenGranter tokenGranter() {
        return new CompositeTokenGranter(getAllTokenGranters());
    }

    /**
     * 所有授权模式：默认的5种模式 + 自定义的模式
     */
    private List<TokenGranter> getAllTokenGranters() {
        OAuth2RequestFactory requestFactory = requestFactory();
        //获取默认的授权模式
        return getDefaultTokenGranters(authServerTokenConfig.getAuthServerToken(clientDetailsService), authCodeInMemoryService, requestFactory);
    }

    private OAuth2RequestFactory requestFactory() {
        return new GlobalOAuth2RequestFactory(clientDetailsService);
    }

    /**
     * 默认的授权模式
     */
    private List<TokenGranter> getDefaultTokenGranters(AuthorizationServerTokenServices tokenServices,
                                                       AuthorizationCodeServices authorizationCodeServices,
                                                       OAuth2RequestFactory requestFactory) {
        List<TokenGranter> tokenGranters = new ArrayList<>();
        // 添加授权码模式
        tokenGranters.add(new AuthorizationCodeTokenGranter(tokenServices, authorizationCodeServices, clientDetailsService, requestFactory));
        // 添加刷新令牌的模式
        tokenGranters.add(new RefreshTokenGranter(tokenServices, clientDetailsService, requestFactory));
        // 添加隐士授权模式
        tokenGranters.add(new ImplicitTokenGranter(tokenServices, clientDetailsService, requestFactory));
        // 添加客户端模式
        tokenGranters.add(new ClientCredentialsTokenGranter(tokenServices, clientDetailsService, requestFactory));
        if (authenticationManager != null) {
            // 添加密码模式
            tokenGranters.add(new GlobalPasswordTokenGranter(authenticationManager, tokenServices, clientDetailsService, requestFactory));
            if (null != grantTypeService) {
                Map<Integer, String> grantTypes = grantTypeService.initGrantTypes();
                if (!CollectionUtils.isEmpty(grantTypes)) {
                    grantTypes.forEach((key, value) -> {
                        if (!AuthGrantTypeEnum.hasType(value)) {
                            tokenGranters.add(new ParamGranter(authenticationManager, tokenServices, clientDetailsService, requestFactory, value));
                        }
                    });
                }
            }
        }
        return tokenGranters;
    }
}
